﻿namespace MyWebLog.Logic.Services
{
    using DatabaseAbstraction.Interfaces;
    using DatabaseAbstraction.Utils;
    using MyWebLog.Logic.Models;
    using MyWebLog.Logic.Models.Security;
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// This service is responsible for dealing with security issues (logins, password resets, user maintenance, etc.)
    /// </summary>
    public class SecurityService : MyWebLogService
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="data">
        /// The database service instance to use
        /// </param>
        public SecurityService(IDatabaseService data) : base(data) { }

        /// <summary>
        /// Attempt to log a user in
        /// </summary>
        /// <param name="email">
        /// The e-mail address to use for the login
        /// </param>
        /// <param name="password">
        /// The password hash to use for the login
        /// </param>
        /// <param name="blog">
        /// The blog to which the user is authenticating
        /// </param>
        /// <returns>
        /// A filled blog authorization, or null if the login fails
        /// </returns>
        public BlogAuthorization Login(string email, string password, Blog blog)
        {
            var loginData = GetLogin(email, password, blog.ID);

            if (0 == loginData.Count)
                return null;

            var authorization = new BlogAuthorization();

            authorization.Blog = blog;
            authorization.User = GetUser(loginData["blog_user_id"]);
            authorization.Permission = GetPermission(loginData["permission_id"]);

            if ((null == authorization.User) || (null == authorization.Permission))
                return null;

            UpdateLastSeen(authorization.User.ID, DateTime.Now);

            return authorization;
        }

        /// <summary>
        /// Get login information for this user
        /// </summary>
        /// <param name="email">
        /// The e-mail address to use for the login
        /// </param>
        /// <param name="password">
        /// The password hash to use for the login
        /// </param>
        /// <param name="blogID">
        /// The ID of the blog to which the user is authenticating
        /// </param>
        /// <returns>
        /// The user ID and permission ID for a successful login; an empty list otherwise
        /// </returns>
        protected Dictionary<string, int> GetLogin(string email, string password, int blogID)
        {
            var parameters = new Dictionary<string, object>();

            parameters.Add("email_address", email);
            parameters.Add("password_hash", password);
            parameters.Add("blog_id", blogID);

            var returnParameters = new Dictionary<string, int>();

            using (var reader = Data.Select("security.login", parameters))
            {
                if (reader.Read())
                {
                    returnParameters.Add("blog_user_id", reader.GetInt32(reader.GetOrdinal("blog_user_id")));
                    returnParameters.Add("permission_id", reader.GetInt32(reader.GetOrdinal("permission_id")));
                }
            }

            return returnParameters;
        }

        /// <summary>
        /// Get a user (by ID)
        /// </summary>
        /// <param name="userID">
        /// The ID of the user to retrieve
        /// </param>
        /// <returns>
        /// A blog user object, or null if the ID is not found
        /// </returns>
        public BlogUser GetUser(int userID)
        {
            using (var reader = Data.Select("security.user.get",
                DbUtils.SingleParameter("blog_user_id", userID)))
            {
                if (reader.Read())
                    return new BlogUser(reader);
            }

            return null;
        }

        /// <summary>
        /// Get a permission (by ID)
        /// </summary>
        /// <param name="permissionID">
        /// The ID of the permission to retrieve
        /// </param>
        /// <returns>
        /// A permission object, or null if the ID is not found
        /// </returns>
        public Permission GetPermission(int permissionID)
        {
            using (var reader = Data.Select("security.permission.get",
                DbUtils.SingleParameter("permission_id", permissionID)))
            {
                if (reader.Read())
                    return new Permission(reader);
            }

            return null;
        }

        /// <summary>
        /// Update a user's last seen date/time
        /// </summary>
        /// <param name="userID">
        /// The ID of the user being updated
        /// </param>
        /// <param name="currentTime">
        /// The date/time to use as the last seen time
        /// </param>
        public void UpdateLastSeen(int userID, DateTime currentTime)
        {
            var parameters = new Dictionary<string, object>();

            parameters.Add("blog_user_id", userID);
            parameters.Add("last_seen_date", currentTime);

            Data.Update("security.user.update.last_seen", parameters);
        }
    }
}